import { FunctionLocalRequest } from './request/FunctionLocalRequest';
import { FunctionRequest } from "./request/FunctionRequest";
import agconnect from "@hw-agconnect/api-ohos";
import "@hw-agconnect/core-ohos";
import "@hw-agconnect/credential-ohos";
import "@hw-agconnect/auth-ohos";
import { FunctionResultImpl } from '../Impl/FunctionResultImpl';
import { AGCFunctionErrorCode } from "../exception/AGCFunctionErrorCode";
import { AGCFunctionException } from "../exception/AGCFunctionException";
import { Logger } from "@hw-agconnect/base-ohos";
import { Backend } from '@hw-agconnect/credential-ohos'
import { FunctionResult } from "../../../../types";

export class FunctionBackend {
  /**
   * client token失效
   */
  static readonly CLIENT_TOKEN_EXPIRED = 0x0c401001;

  /**
   * access token失效
   */
  static readonly ACCESS_TOKEN_EXPIRED = 0x0c401002;

  /**
   * HTTP Status-Code 401: Token invalid.
   */
  static readonly TOKEN_INVALID = 401;
  static readonly TAG = 'function';
  private identifier;

  constructor(identifier: string) {
    this.identifier = identifier;
  }

  private async getAccessToken(force: boolean): Promise<string | null> {
    let authService = agconnect.auth(this.identifier);
    if (authService) {
      let currentUser = await authService.getCurrentUser();
      if (currentUser) {
        return currentUser.getToken(force).then((res: any) => {
          if (res) {
            return Promise.resolve(res.getToken());
          }
          return Promise.resolve(null);
        })
      }
    }
    return Promise.resolve(null);
  }

  private async doResponse(response: any): Promise<FunctionResult> {
    let responseClass = new FunctionResultImpl();
    responseClass.ret.setCode(0);
    responseClass.ret.setMsg('OK');
    let result = undefined;
    try {
      result = JSON.parse(response);
    } catch (err) {
      result = response;
    }
    responseClass.constructResponse(result);
    return Promise.resolve(responseClass);
  }

  public async doRequest(functionRequest: FunctionRequest, timeoutMillis: number | undefined, refreshClientToken: boolean,
                         refreshAccessToken: boolean): Promise<FunctionResult> {
    // 如果用户使用了auth
    let accessToken = await this.getAccessToken(false);
    if (accessToken) {
      functionRequest.setAccessToken(accessToken);
    }
    // 设置CT
    let credentialService = agconnect.credential(this.identifier);
    if (!credentialService) {
      Logger.error(FunctionBackend.TAG, "get credentialProvider service failed.");
      return Promise.reject(new AGCFunctionException(AGCFunctionErrorCode.FAIL_TO_GET_CREDENTIAL_SERVICE));
    }
    let clientToken = await credentialService.getToken();
    let authorization = 'Bearer ' + clientToken.tokenString;
    functionRequest.setAuthorization(authorization);

    let option = undefined;
    if (timeoutMillis) {
      option = {
        readTimeout: timeoutMillis,
        connectTimeout: timeoutMillis
      }
    }
    let response = await Backend.post(functionRequest, option).catch(async error => {
      if (error.responseCode) {
        let resultJson;
        try {
          resultJson = JSON.parse(error.result);
        } catch (err) {
          resultJson = error.result;
        }
        let errorCode = resultJson?.ret?.code;
        let errorMsg = resultJson?.ret?.message ? resultJson?.ret?.message : resultJson?.ret?.msg;
        if (error.responseCode === FunctionBackend.TOKEN_INVALID) {
          if (errorCode === FunctionBackend.CLIENT_TOKEN_EXPIRED && refreshClientToken) {
            await credentialService!.removeToken();
            await credentialService!.getToken(true);
            return this.doRequest(functionRequest, timeoutMillis, false, refreshAccessToken);
          }
          if (errorCode === FunctionBackend.ACCESS_TOKEN_EXPIRED && refreshAccessToken) {
            await this.getAccessToken(true);
            return this.doRequest(functionRequest, timeoutMillis, refreshClientToken, false);
          }
        }
        let code = errorCode ? errorCode : error.responseCode;
        let message = errorMsg ? errorMsg : "";
        return Promise.reject(new AGCFunctionException({ "code": code, "message": message }));
      }
      return Promise.reject(error);
    });
    return this.doResponse(response);
  }

  public async doLocalRequest(request: FunctionLocalRequest): Promise<FunctionResult>{
    let response = await Backend.post(request, null).catch(async error => {
      return Promise.reject(error);
    });
    return this.doResponse(response);
  }
}